@toc
函数
1 | function 函数名(参数列表) { |
函数表达式
使用表达式来定义函数,表达式中的函数名可以省略,如果这个函数名不省略,也只能用在此函数内部
匿名函数
1 | const add = function(x, y){ |
有名字的函数表达式
1 | const sub = function fn(x, y) { |
- 内部使用,相当于递归调用
1 | const sub = function fn(n) { |
函数、匿名函数、函数表达式的差异
- 函数和匿名函数,本质上都是一样的,都是函数对象,只不过函数有自己的标识符——函数名,匿名函数需要借助其它的标识符而已
- 区别在于,函数会声明提升,函数表达式不会
1
2
3
4
5
6
7
8
9
10
11
12console.log(add(4,6))
// 匿名函数
function add(x, y){ // 声明提升
return x + y;
};
// console.log(sub(5, 3)); // sub 未定义
// 有名字的函数表达式
const sub = function (x,y){
return x - y;
};
console.log(sub(5, 3));
高阶函数
高阶函数:函数作为参数或返回一个函数
完成一个计数器counter
1
2
3
4
5
6
7
8
9
10
11const counter = function(){
let c = 0
return function(){
return ++c;
};
};
const c = counter()
console.log(c()) // 1
console.log(c()) // 2
console.log(c()) // 3完成一个map函数:可以对某一个数组的元素进行某种处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const map = function(arr,fn){
let newarr = [];
for (let i in arr){
newarr[i] = fn(arr[i]);
}
return newarr
}
console.log(map([1,2,3,4], function(x) {return x++}))
console.log(map([1,2,3,4], function(x) {return ++x}))
console.log(map([1,2,3,4], function(x) {return x+1}))
console.log(map([1,2,3,4], function(x) {return x+=1}))
// 执行结果
[ 1, 2, 3, 4 ]
[ 2, 3, 4, 5 ]
[ 2, 3, 4, 5 ]
[ 2, 3, 4, 5 ]
另附counter的生成器版本,仅供参考1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const counter = (function * () {
let c = 1
while (true) {
yield c++
}
})()
console.log(counter.next())
console.log(counter.next())
console.log(counter.next())
// 执行结果
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
箭头函数
箭头函数就是匿名函数,它是一种更加精简的格式。
将上例中的函数更改为箭头函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17const map = function(arr,fn){
let newarr = [];
for (let i in arr){
newarr[i] = fn(arr[i]);
}
return newarr
}
// 以下三行等价
console.log(map([1,2,3,4], (x) => {return x*2}));
console.log(map([1,2,3,4], x => {return x*2}));
console.log(map([1,2,3,4], x => x*2));
// 执行结果
[ 2, 4, 6, 8 ]
[ 2, 4, 6, 8 ]
[ 2, 4, 6, 8 ]
箭头函数参数
- 如果一个函数没有参数,使用
()
- 如果只有一个参数,参数列表可以省略小括号
()
- 多个参数不能省略小括号,且使用逗号间隔
箭头函数返回值
- 如果函数体部分有多行,就需要使用
{}
,如果有返回值使用return
- 如果只有一行语句,可以同时省略大括号和return
- 只要有return语句,就不能省略大括号
console.log(map([1,2,3,4], x => {return ++x}))
,有return必须有大括号 - 如果只有一条非return语句,加上大括号,函数就成了无返回值
console.log(map([1,2,3,4], x => {x*2}));
加上了大括号,它不等价于x =>{return x*2}
因此,记住x => x*2
这种正确的形式就行函数参数
普通参数
- 一个参数占一个位置,支持默认参数
1
2
3
4
5
6
7const add = (x, y) => x + y
console.log(add(4, 5)) // 9
// 缺省值
const add1 = (x, y=5) => x + y
console.log(add1(4, 7)) // 11
console.log(add1(4)) // 9
测试下面代码1
2
3
4
5
6const add = (x=6, y) => x + y
console.log(add()) // NaN
console.log(add(1)) // NaN
console.log(add(y=2, z=3)) // 5
console.log(add(3, 4, 5, 6)) // 7
观察上面diamagnetic执行结果,原因
- JS中并没有Python中的关键字传参
- JS只是做参数位置的对应
- JS并不限制默认参数的位置
- JS传参个数超范围,多余的参数不影响函数调用
add2(y=2,z=3)
相当于add2(2,3)
,因为JS没有关键字传参,但是它的赋值表达式有值值,y=2就是2,z=3就是3
建议,默认参数写到后面,这是一个好的习惯
可变参数(rest parameters剩余参数)
JS使用…表示可变参数(Python用*收集多个参数)1
2
3
4
5
6
7
8
9
10const sum = function (...args){
let result = 0;
console.log(...args) // 3 6 9 参数解构
for (let x in args){
result += args[x]
};
return result
};
console.log(sum(3, 6, 9)) // 18
arguments对象
函数的所有参数会被保存在一个arguments的键值对对象中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18(function (p1, ...args) {
console.log(p1)
console.log(args)
console.log('----------------')
console.log(arguments) // 对象
for (let x of arguments) // 该对象可以使用of
console.log(x);
})('abc', 1,3,5)
// 执行结果
abc
[ 1, 3, 5 ]
----------------
[Arguments] { '0': 'abc', '1': 1, '2': 3, '3': 5 }
abc
1
3
5
ES6之前,arguments是唯一可变参数的实现。
ES6开始,不推荐,建议使用可变参数。为了兼容而保留。
注意,使用箭头函数,取到的arguments不是我们想要的,如下1
2
3
4
5((x, ...args) => {
console.log(args);
console.log(x);
console.log(arguments);
})(...[1,2,3,4])
参数解构
和Python类似,Js提供了参数解构,依然使用了...
符号来解构1
2
3
4const add = (x, y) => {console.log(x,y);return x + y};
console.log(add(...[100,200]))
console.log(add(...[100,200,300,3,5,3]))
console.log(add(...[100]))
Js支持参数解构,不需要解构后的值个数和参数个数对应
函数返回值
python 中可以使用 return 1,2
返回多值,本质上也是一个值,就是一个元组。Js中呢1
2const add = (x, y) => {return x,y};
console.log(add(4,100)); // 100
表达式的值
- 类C的语言,都有一个概念——表达式的值
- 赋值表达式的值:等号右边的值
- 逗号表达式的值:类C语言,都支持逗号表达式,逗号表达式的值,就是最后一个表达式的值
1
2
3
4
5
6
7
8
9
10
11a = (x = 5, y = 6, true);
console.log(a); //true
b = (123, true, z = 'test')
console.log(b) // test
function c() {
return x = 5, y = 6, true, 'ok';
}
console.log(c()); // ok
所以,JS的函数返回值依然是单值
作用域
1 | // 函数中变量的作用域 |
function是函数的定义,是一个独立的作用域,其中定义的变量在函数外不可见
- var a = 100 可以提升声明,也可以突破非函数的块作用域
- a = 100 隐式声明不能提升声明,在“严格模式”下会出错,但是可以把变量隐式声明为全局变量。建议少用
- let a = 100 不能提升声明,而且不能突破任何的块作用域。推荐使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75function show(i, arg) {
console.log(i, arg)
}
// 作用域测试
x = 500;
var j = 'jjjj';
var k = 'kkkk';
function fn(){
let z = 400;
{
var o = 100; // var 作用域当前上下文
show(1, x);
t = 'free'; // 此语句执行后,t作用域就是全局的,不推荐
let p = 200;
}
var y = 300;
show(2,z);
show(3,x);
show(4,o);
show(5,t);
//show(6,p); // 异常,let出不来上一个语句块
{
show(7,y);
show(8,o);
show(9,t);
{
show(10,o);
show(11,t);
show(12,z);
}
}
j = 'aaaa';
var k = 'bbbb';
show(20, j);
show(21, k);
}
// 先执行函数
fn()
show(22, j);
show(23, k);
//show(13,y); // 异常,y只能存在于定义的上下文中,出不了函数
show(14,t); // 全局,但是严格模式会抛异常
//show(15,o) // 看不到o,异常原因同y
show(16,z); // 变量声明提升,var声明了z,但是此时还没有赋值
var z = 10;
const m = 1
//m = 2 // 常量不可以重新赋值
// 执行结果
1 500
2 400
3 500
4 100
5 'free'
7 300
8 100
9 'free'
10 100
11 'free'
12 400
20 'aaaa'
21 'bbbb'
22 'aaaa'
23 'kkkk'
14 'free'
16 undefined
严格模式
- 使用”use strict”;,这条语句放到函数的首行,或者js脚本首行
异常
抛出异常
Js的异常语法和Java相同,使用throw关键字抛出。
使用throw关键字可以抛出任意对象的异常1
2
3
4
5
6
7throw new Error('new error');
throw new ReferenceError('Ref Error');
throw 1;
throw 'not ok';
throw [1, 2, 3];
throw {'a':1};
throw () => {}; // 函数
捕获异常
try...catch
语句捕获异常try...catch...finally
语句捕获异常,finally
保证最终一定执行
- 注意这里的
catch
不支持类型,也就是说至多一个catch语句 - 可以在catch的语句块内,自行处理异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16try {
//throw new Error('new error');
//throw new ReferenceError('Ref Error');
//throw 1;
//throw new Number(100);
// throw 'not ok';
// throw [1,2,3];
// throw {'a':1};
throw () => {}; // 函数
} catch (error) {
console.log(error);
console.log(typeof(error));
console.log(error.constructor.name);
} finally {
console.log('===end===')
}